﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using GameEngine.View.Components;
using MapEngine;
using System.Windows.Shapes;

namespace GameEngine.Clients.Silverlight.View.Components
{
    public partial class MapUC : UserControl, IMapView<int, FrameworkElement>
    {
        public Point MapSize { get; set; }
        public float MaxMapHeight { get; set; }
        public float MinMapHeight { get; set; }
        public float TileHeightUnit { get; set; }

        public Point MapPosition
        {
            get { return TileMap.Position; }
            set { TileMap.Position = value; }
        }
        public TileMapUC TileMap;
        public List<UIElement> Items = new List<UIElement>();
        public Point TileSize { get; set; }
        GetHeightDelegate GetHeightExternal;
        public TileType TileType { get; set; }
        public MapType MapType { get; set; }

        public event Action<Point> OnLayoutUpdate;
        public event TileMouseButtonEventDelegate TileMouseLeftButtonUp;
        public event TileMouseButtonEventDelegate TileMouseRightButtonUp;
        public event ItemMouseButtonEventDelegate<UIElement> ItemMouseLeftButtonUp;
        public event ItemMouseButtonEventDelegate<UIElement> ItemMouseRightButtonUp;
        public event TileMouseEventDelegate TileMouseMove;
        public event TileMouseEventDelegate TileMouseEnter;
        public event TileMouseEventDelegate TileMouseLeave;

        public MapUC()
        {
            InitializeComponent();
        }

        public void Initialize(GetHeightDelegate getHeight)
        {
            GetHeightExternal = getHeight;
            //MapType = MapType.Isometric;  //TODO tem k vir em parametro          
            TileType = TileType.Square;
            //MaxMapHeight = (float)TileSize.X * 3;
            //MaxMapHeight = 100;
            
            TransformationGrid.SizeChanged += MapContainer_LayoutUpdated;
            UpdateMap();
        }

        void MapContainer_LayoutUpdated(object sender, EventArgs e)
        {
            LayoutRoot.Clip = new RectangleGeometry()
            {
                Rect = new Rect(0, 0, MapContainer.ActualWidth, MapContainer.ActualHeight)
            };
            //if (TileMap != null)
            //{
            //    TileMap.Height = MapContainer.ActualHeight;
            //    TileMap.Width = MapContainer.ActualWidth;
            //}
            if (OnLayoutUpdate != null)
                OnLayoutUpdate(new Point(Width, Height));
        }

        public void ScaleMap(float scale)
        {
            ScaleTransform.CenterX = MapContainer.ActualWidth / 2;
            ScaleTransform.CenterY = MapContainer.ActualHeight / 2;

            ScaleTransform.ScaleX = scale;
            ScaleTransform.ScaleY = scale;
        }

        Point oldPoint;
        public void MoveMap(Point p)
        {
            if (oldPoint == null)
                oldPoint = p;
            TileMap.MoveMap(
                (int)(p.Y - oldPoint.Y > 0 ? p.Y - oldPoint.Y : 0),
                (int)(p.X - oldPoint.X > 0 ? p.X - oldPoint.X : 0),
                (int)(oldPoint.Y - p.Y > 0 ? oldPoint.Y - p.Y : 0),
                (int)(oldPoint.X - p.X > 0 ? oldPoint.X - p.X : 0)
                );
            oldPoint = p;
        }

        public void UpdateMapPosition()
        {
            Point Translation = new Point();

            //Pôr mapa no centro do ecra
            Point ScreenPosition = new Point(MapContainer.ActualWidth / 2, MapContainer.ActualHeight / 2);
            Translation.X = ScreenPosition.X;
            Translation.Y = ScreenPosition.Y;

            //Centrar mapa
            //Point ScreenTileSize = TileMap.Transform(TileSize,0);
            //Translation.X += ScreenTileSize.X * MapSize.X / 2;
            //Translation.Y += ScreenTileSize.Y * MapSize.Y / 2;

            //Pôr mapa na posição corrente (camara)
            Point ScreenTranslation = TileMap.Transform(new Point(
                -TileMap.Position.X * TileSize.X,
                -TileMap.Position.Y * TileSize.Y
                ), 0);
            Translation.X += ScreenTranslation.X;
            Translation.Y += ScreenTranslation.Y;

            TranslateTransform.X = Translation.X;
            TranslateTransform.Y = Translation.Y;
        }

        public void MouseOnItems(bool value)
        {
            foreach (UIElement item in Items)
            {
                item.IsHitTestVisible = value;
            }
        }

        public void UpdateUIElement(IThingView<int, FrameworkElement> element, Point mapPosition, float height)
        {
            var itemUC = element as ItemUC;
            TransformGroup tg = new TransformGroup();
            TranslateTransform tt = new TranslateTransform();
            Point screenPosition = TileMap.TransformMapPositionToScreenPosition(mapPosition, height);
            tt.X = screenPosition.X;
            tt.Y = screenPosition.Y;
            tg.Children.Add(tt);
            itemUC.RenderTransform = tg;

            Canvas.SetZIndex(itemUC,
                (int)((MapSize.X * MapSize.Y)
                - mapPosition.Y
                + mapPosition.X
                + height));
        }

        public void AddUIElement(IThingView<int, FrameworkElement> element, Point mapPosition, float height)
        {
            var itemUC = element as ItemUC;
            itemUC.IsHitTestVisible = false;

            MapContainer.Children.Add(itemUC);

            Items.Add(itemUC);

            element.MouseLeftButtonUp += new MouseButtonEventHandler(element_MouseLeftButtonUp);

            UpdateUIElement(element, mapPosition, height);
        }

        public void RemoveUIElement(IThingView<int, FrameworkElement> element)
        {
            MapContainer.Children.Remove(element as ItemUC);
            Items.Remove(element as ItemUC);
        }

        public void UpdateMap()
        {
            MapContainer.Children.Remove(TileMap);

            TileMap = new TileMapUC(
                GetHeight,
                TileSize,
                CreateShapes,
                MapSize,
                new Point(MapContainer.ActualWidth / 2, MapContainer.ActualHeight / 2),
                TileType);
            TileMap.MouseLeave += new MouseEventHandler(TileMap_MouseLeave);
            TileMap.MouseMove += new MouseEventHandler(TileMap_MouseMove);
            TileMap.MouseLeftButtonUp += new MouseButtonEventHandler(TileMap_MouseLeftButtonUp);


            /*
            foreach (Tile tile in TileMap.Tiles) {
                tile.MouseEnter += new MouseEventHandler(tile_MouseEnter);
                tile.MouseLeave += new MouseEventHandler(tile_MouseLeave);
                tile.MouseLeftButtonUp += new MouseButtonEventHandler(tile_MouseLeftButtonUp);
                tile.MouseMove += new MouseEventHandler(tile_MouseMove);
            }*/


            //Map.Children.Add(new TextBox());
            //Translate.Y = MapIndexSize.Y * TileSize.Y;
            //TranslateTransform.Y = Position.Y * TileSize.Y;

            MapContainer.Children.Add(TileMap);
            //TileMap.Width = MapContainer.ActualWidth;
            //TileMap.Height = MapContainer.ActualHeight;
            /* TileMap MiniMap = new TileMap(GetHeight, new Point(5, 5), GetColor, MapSize,new Point(5,5));
             MiniMap.Type = Tile.TileType.Square;
             for (int i = 0; i < MapSize.Y; i++)
             {
                 MiniMap.AddRow(1, (int)MapSize.X);
             }*/
            //MiniMapContainer.Children.Add(MiniMap);

            //TransformGroup tg = new TransformGroup();
            //TranslateTransform translate = new TranslateTransform()
            //{
            //    X = MapContainer.ActualWidth / 2,//- MapSize.X*TileSize.X/*/2*/,
            //    Y = MapContainer.ActualHeight / 2 //+ MapSize.Y * TileSize.Y/ 2
            //};
            //tg.Children.Add(translate);
            //Map.RenderTransform = tg;
            UpdateMapPosition();

            /*Point one = TileMap.Transform(new Point(-100, 0), 0);
            Point two = TileMap.Transform(new Point(100, 0), 0);

            Point three = TileMap.Transform(new Point(0, 100), 0);
            Point four = TileMap.Transform(new Point(0, -100), 0);

            Point five = TileMap.Transform(new Point(0, 0), 100);
            Point six = TileMap.Transform(new Point(0, 0), -100);

            MapContainer.Children.Add(new Line() { X1 = one.X, X2 = two.X, Y1 = one.Y, Y2 = two.Y, Stroke = new SolidColorBrush(Colors.Green), StrokeThickness = 1 });
            MapContainer.Children.Add(new Line() { X1 = three.X, X2 = four.X, Y1 = three.Y, Y2 = four.Y, Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 1 });
            MapContainer.Children.Add(new Line() { X1 = five.X, X2 = six.X, Y1 = five.Y, Y2 = six.Y, Stroke = new SolidColorBrush(Colors.Blue), StrokeThickness = 1 });
            */
        }

        private void Dcw_Closed(object sender, EventArgs e)
        {
            /*
            if (MapOptionsWindow.DialogResult != null && MapOptionsWindow.DialogResult.Value)
            {
                Frequency = float.Parse(MapOptionsWindow.Frequency.Text);
                Amplitude = float.Parse(MapOptionsWindow.Amplitude.Text);
                Persistence = float.Parse(MapOptionsWindow.Persistence.Text);
                Octaves = int.Parse(MapOptionsWindow.Octaves.Text);
                Seed = int.Parse(MapOptionsWindow.Seed.Text);
                TileSize = new Point(
                    double.Parse(MapOptionsWindow.TileSizeX.Text),
                    double.Parse(MapOptionsWindow.TileSizeY.Text));
                MaxMapHeight = float.Parse(MapOptionsWindow.MaxHeight.Text);
                CreateMap();
            }*/
        }

        void ShowWindow()
        {
            /*
            MapOptionsWindow.Frequency.Text = Frequency.ToString();
            MapOptionsWindow.Amplitude.Text = Amplitude.ToString();
            MapOptionsWindow.Persistence.Text = Persistence.ToString();
            MapOptionsWindow.Octaves.Text = Octaves.ToString();
            MapOptionsWindow.Frequency.Text = Frequency.ToString();
            MapOptionsWindow.Seed.Text = Seed.ToString();
            MapOptionsWindow.TileSizeX.Text = TileSize.X.ToString();
            MapOptionsWindow.TileSizeY.Text = TileSize.Y.ToString();
            MapOptionsWindow.MaxHeight.Text = MaxMapHeight.ToString();
            MapOptionsWindow.Show();*/
        }

        //static Vector3 sun = new Vector3(10, 10, 10);
        List<Shape> CreateShapes(Tile tile)
        {
            List<Shape> shapes = new List<Shape>();
            foreach (List<Point3d> vertices in tile.ShapeVertices)
            {
                Polygon shape = new Polygon();
                foreach (Point3d corner in vertices)
                {
                    switch (MapType)
                    {
                        case MapType.Top:
                            shape.Points.Add(new Point(corner.Position.X, corner.Position.Y));
                            break;
                        case MapType.Isometric:
                            shape.Points.Add(IsometricMath.PointFrom3d(new Point3d(new Point(corner.Position.X, -corner.Position.Y), corner.Height)));
                            break;
                        //case TileType.Triangles:
                        //    Vertices = BuildIsometricTriangles();
                        //    break;
                        // case TileType.IsometricTriangles:
                        //    Vertices = BuildIsometricTriangles();
                        //    break;
                    }
                }
                Color color;
                if (tile.Height < 1 * MaxMapHeight / 100)
                    color = Colors.Blue;
                else
                    if (tile.Height < 10 * MaxMapHeight / 100)
                        color = Colors.Yellow;
                    else
                        if (tile.Height < 70 * MaxMapHeight / 100)
                            color = Colors.Green;
                        else
                            if (tile.Height < 90 * MaxMapHeight / 100)
                                color = Colors.Gray;
                            else
                                color = Colors.White;
                //return new SolidColorBrush(color);

                SolidColorBrush colorBrush = new SolidColorBrush(color);
                shape.Fill = colorBrush;
                shape.StrokeThickness = 0;
                //TODO square.CacheMode
                //square.Stroke = square.Fill;
                //square.Stroke = new SolidColorBrush(Colors.Black);
                shapes.Add(shape);
            }
            return shapes;

            /*
             plane.Points[0].
            Vector3 normal = Vector3.CrossProduct(
                new Vector3((float)pSouth.X, heightS, (float)pSouth.Y) - new Vector3((float)pCenter.X, height, (float)pCenter.Y),
                new Vector3((float)pWest.X, heightW, (float)pWest.Y) - new Vector3((float)pCenter.X, height, (float)pCenter.Y));
            float angle = (Vector3.DotProduct(sun, normal));
            t2.Fill = new SolidColorBrush(Color.FromArgb(255, 0, byte.Parse("" + (Math.Round(angle * 255 / 360))), 0));
            return new SolidColorBrush(Colors.Green);*/
        }

        float? GetHeight(Point position)
        {
            //return MaxMapHeight * (Generator.Compute((float)(position.X / TileSize.X), (float)(position.Y / TileSize.Y), 0));

            return GetHeightExternal(position);
        }

        #region Events
        void element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (ItemMouseLeftButtonUp != null)
                ItemMouseLeftButtonUp(sender as UIElement, e);
        }

        void TileMap_MouseLeave(object sender, MouseEventArgs e)
        {
            if (TileMouseLeave != null)
                if (lastTile != null)
                    TileMouseLeave(lastTile.Value, e);
            lastTile = null;
        }

        void TileMap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (TileMouseLeftButtonUp != null)
            {
                Point p = e.GetPosition(TileMap);
                float height;
                Point? position = TileMap.TransformScreenPositionToMapPosition(p, out height);
                if (position != null)
                    TileMouseLeftButtonUp(position.Value, e);
            }
        }

        Point? lastTile = null;
        void TileMap_MouseMove(object sender, MouseEventArgs e)
        {
            if (TileMouseMove != null || TileMouseEnter != null || TileMouseLeave != null)
            {
                Point p = e.GetPosition(TileMap);
                float height;
                Point? position = TileMap.TransformScreenPositionToMapPosition(p, out height);
                if (position != null)
                {
                    if (TileMouseMove != null)
                        TileMouseMove(position.Value, e);

                    if (lastTile == null)
                    {
                        if (TileMouseEnter != null)
                            TileMouseEnter(position.Value, e);
                    }
                    else
                    {
                        if (!lastTile.Equals(position))
                        {
                            if (TileMouseLeave != null)
                                TileMouseLeave(position.Value, e);
                            if (TileMouseEnter != null)
                                TileMouseEnter(position.Value, e);
                        }
                    }
                }
                else
                {
                    if (TileMouseLeave != null)
                        if (lastTile != null)
                            TileMouseLeave(lastTile.Value, e);
                }
                lastTile = position;
            }
        }

        void tile_MouseMove(object sender, MouseEventArgs e)
        {
            Tile tile = sender as Tile;
            if (TileMouseMove != null)
                TileMouseMove(tile.MapPosition, e);
        }

        void tile_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Tile tile = sender as Tile;
            if (TileMouseLeftButtonUp != null)
                TileMouseLeftButtonUp(tile.MapPosition, e);
        }

        void tile_MouseLeave(object sender, MouseEventArgs e)
        {
            Tile tile = sender as Tile;
            if (TileMouseLeave != null)
                TileMouseLeave(tile.MapPosition, e);
        }

        void tile_MouseEnter(object sender, MouseEventArgs e)
        {
            Tile tile = sender as Tile;
            if (TileMouseEnter != null)
                TileMouseEnter(tile.MapPosition, e);
        }
        #endregion

        public void UpdateTile(Point point)
        {
            TileMap.UpdateTile(point);
        }

        public Point ViewSize
        {
            get
            {
                return new Point(Width, Height);
            }
            set
            {
                Width = value.X;
                Height = value.Y;
            }

        }
    }
}